---
description: Learn how use a schema in your Triplit app to enable Typescript support and data validation.
---

import { Callout } from 'nextra-theme-docs';

# Schemas

## Schemaful vs Schemaless

Providing a schema to Triplit is optional, **but it is recommended** in order to
take advantage of all the features provided by Triplit.

Limitations of schemaless mode include:

- You are limited to exclusively using storing value types that are supported by JSON: string, number, boolean, objects, null.
- If you use Typescript, you will not get type checking for your queries and results.
- [Access control rules](/schemas/permissions) are defined in schemas, and thus are not supported in schemaless mode.

## Defining your schema

A schema object defines your collections and the attributes and relationships on those collections. Schemas are defined in Javascript like so:

```typescript
import { Schema as S, TriplitClient } from '@triplit/client';

const schema = S.Collections({
  todos: {
    schema: S.Schema({
      id: S.Id(),
      text: S.String(),
      complete: S.Boolean(),
      created_at: S.Date(),
      tags: S.Set(S.String()),
    }),
  },
  users: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
      address: S.Record({
        street: S.String(),
        city: S.String(),
        state: S.String(),
        zip: S.String(),
      }),
    }),
  },
});

const client = new TriplitClient({
  schema,
});
```

Passing a schema to the client constructor will override any schema currently stored in your cache. This can cause data corruption if the new schema is not compatible with existing data in the shape of the old schema. Refer to the [schema management guide](/schemas/updating) for more information.

<Callout>
  By default, your schema file will be created by `triplit init` or `npm create
  triplit-app` in your project directory at `triplit/schema.ts`. If you need to
  save your schema file somewhere else, you can specify that path with the
  `TRIPLIT_SCHEMA_PATH` environmental variable and the Triplit CLI commands will
  refer to it there.
</Callout>

### id

Every collection in Triplit must define an `id` field in its schema. The `S.Id()` data type will generate a random `id` by default upon insertion. You can specify the format of the generated ID using the `format` option.

#### ID Generation Formats

Triplit supports three ID generation formats:

- **`nanoid`** (default): Generates a 21-character URL-safe ID. Pre-installed on both client and server.
- **`uuidv4`**: Generates a standard UUID v4. Uses native functionality, no package required.
- **`uuidv7`**: Generates a UUID v7 with timestamp ordering. Requires the `uuidv7` package on the client (pre-installed on server).

```typescript
// Using different ID formats
const schema = S.Collections({
  todos: {
    schema: S.Schema({
      id: S.Id({ format: 'nanoid' }), // default
      // ... other fields
    }),
  },
  users: {
    schema: S.Schema({
      id: S.Id({ format: 'uuidv4' }),
      // ... other fields
    }),
  },
  posts: {
    schema: S.Schema({
      id: S.Id({ format: 'uuidv7' }),
      // ... other fields
    }),
  },
});
```

If you want to specify the `id` for each entity, you may pass it **as a string** in to the `insert` method as shown below.

```typescript
// assigning the id automatically
await client.insert('todos', {
  text: 'get tortillas',
  complete: false,
  created_at: new Date(),
  tags: new Set(['groceries']),
})

// assigning the id manually
await client.insert('todos', {
  id: 'tortillas'
  text: 'get tortillas',
  complete: false,
  created_at: new Date(),
  tags: new Set(['groceries']),
})
```

<Callout type="info">
  For `uuidv7` format, you need to install the `uuidv7` package on the client:
  ```bash npm install uuidv7 ``` The server has this package pre-installed.
</Callout>

### Getting types from your schema

While the `schema` passed to the client constructor will be used to validate your queries and give you type hinting in any of the client's methods, you may want to extract the types from your schema to use in other parts of your application.

#### `Entity`

You can extract a simple type from your schema with the `Entity` type.

```typescript
import { type Entity, Schema as S } from '@triplit/client';

const schema = S.Collections({
  todos: {
    schema: S.Schema({
      id: S.Id(),
      text: S.String(),
      complete: S.Boolean(),
      created_at: S.Date(),
      tags: S.Set(S.String()),
    }),
  },
});

type Todo = Entity<typeof schema, 'todos'>;
/* 
Todo will be a simple type:
{ 
  id: string, 
  text: string, 
  complete: boolean, 
  created_at: Date, 
  tags: Set<string> 
} 
*/
```

#### `QueryResult`

If you need more advanced types, e.g. that include an entity's relationships, you can use the `QueryResult` type. It allows you to generate the return type of any query, e.g. with a `Select` clause that narrows fields or `Include` clauses that add related entities.

```ts
import { type QueryResult, Schema as S } from '@triplit/client';

const schema = S.Collections({
  users: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
    }),
    relationships: {
      posts: S.RelationMany('posts', {
        where: [['authorId', '=', '$1.id']],
      }),
    },
  },
  posts: {
    schema: S.Schema({
      id: S.Id(),
      text: S.String(),
      authorId: S.String(),
    }),
  },
});

type UserWithPosts = QueryResult<
  typeof schema,
  { collectionName: 'users'; select: ['name']; include: { posts: true } }
>;

/*
type UserWithPosts = {
  name: string;
  posts: Array<{
    id: string;
    text: string;
  }>
}
*/
```

### Reading your schema

Your schema is available in your codebase in the `triplit/schema.ts` file. However you may locally edit the schema, or you may not be aware of remote edits that have happened to the schema. To view the current state of the server's schema, run:

```bash
triplit schema print -l remote -f file
```

See [CLI docs](/cli/schema#triplit-schema-print) or run `triplit schema print --help` for more options.
